#include<iostream>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <cstring>
#include <unistd.h>
#include <pthread.h>

#define NUM 5
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; // pthread_mutex_t 原生线程库提供的一个数据类型
using namespace std;
//其实在代码中的一句话，其实对应汇编语言中是不止一句语句的。而线程有可能在任何时刻被调度器切换，此时，对于不加保护内存中的数据，可能就会出现数据不一致的问题
//为解决多线程导致的数据不一致问题，就引入锁（lock）来保护
class threadData
{
public:
    threadData(int number,pthread_mutex_t* pm)
    {
        threadname = "thread-" + to_string(number);
        mtx=pm;
    }

public:
    string threadname;
    pthread_mutex_t *mtx;
};
// class threadData
// {
// public:
//     threadData(int number)
//     {
//         threadname = "thread-" + to_string(number);
//     }

// public:
//     string threadname;
// };
int ticket = 1000;
//对ticket这样的全局数据进行保护，就叫加锁。常用pthread_mutex_init
//锁的初始化有两种方法
//1.调用初始化接口
//2.直接把一个宏赋值给锁
void *route(void *args)
{
    threadData *td = static_cast<threadData *>(args); // 强制类型转换和（threadData*）args一样，只是这样写更优雅
    // pthread_detach(pthread_self());
    const char *name = td->threadname.c_str();
    while (true)
    {
        //// 线程对于锁的竞争能力可能会不同
        //***这部分代码对临界资源的访问叫做临界区(加锁和解锁之间的部分)，我们也就是要对这部分代码进行保护
        pthread_mutex_lock(&mtx);
        //这句代码保证了，只有拿到锁的线程才能向后执行，同时这个接口也保证了没有拿到锁的线程（锁还在其他线程手里）就要在这里阻塞等待
        //
        if (ticket > 0)//这里的比较也是在cpu（逻辑运算和数值运算）里执行的
        {
            usleep(10000);//模拟漫长业务的过程(信息填写)，在这个漫长的业务过程中，可能有很多个线程会进入该代码段
            printf("who=%s, get a ticket: %d\n", name, ticket); 
            ticket--;
            pthread_mutex_unlock(&mtx);
        }
        //***
        else
        {
            pthread_mutex_unlock(&mtx);
            break;
        }
        //加锁到解锁这里面的代码只能串行运行，不能并行
        //注意解锁不要放在if，else外面，防止if，else里面就跳出了，这样就没有解锁。
        usleep(13); // 我们抢到了票，我们会立马抢下一张吗？其实多线程还要执行得到票之后的后续动作。usleep模拟
    }
    printf("%s ... quit\n", name);
    return nullptr;
}
//为什么加锁不加在while前面 解锁放在while结束之后?
//如果是这样，整个抢票的逻辑就完全串行了 这和没有多线程有什么区别? 所以我们加锁的时候，一定要保证加锁的粒度，越小越好!
int main(void)
{
    pthread_mutex_t mtx;
    pthread_mutex_init(&mtx, nullptr);
    vector<pthread_t> tids;
    for (int i = 0; i < NUM; i++)
    {
        pthread_t tid;
        //threadData *td = new threadData(i);
        threadData *td = new threadData(i,&mtx);
        pthread_create(&tid, NULL, route, td);
        tids.push_back(tid);
    }

    for(const auto& tid:tids)
    {
        pthread_join(tid,nullptr);
    }
    return 0;
}
//加了锁之后，线程在临界区中，是否会切换?
// 会切换，会有问题吗?不会!
// 虽然被切换，但是我们是持有锁被切换的!
// 其他执行流想要执行这部分代码，要申请锁，因此其他执行流申请锁会失败
// 加锁就是串行执行了吗?
// 是的!执行临界区代码一定是串行的
// 要访问临界资源，每一个线程都必须申请锁，前提是，每一个线程都必须先看到同一把锁 && 去访问它 那么，锁本身是不是一种共享临界资源? 谁来保证锁的安全呢? 所以为了保证锁的安全，申请和释放锁必须是原子的!!!
// 如何保证?锁究竟是什么?锁是如何实现的?
